/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_real_var.h                                                         *
 ****************************************************************************/

/**
 * @file elisa_real_var.h
 * @brief Classes for real variables.
 */

/**
 * @defgroup RealConstraint Real Constraints
 */

#ifndef _elisa_real_var_h_
#define _elisa_real_var_h_

#include <stdlib.h>
#include <string>
#include <iostream>

#include "elisa_variable.h"
#include "elisa_interval.h"
#include "elisa_defs.h"

NAMESPACE_ELISA

//forward declaration
class RealVar;
class RealVarRep;


/**
 * @ingroup RealConstraint
 * @class RealVar
 * @brief Constrained interval variable
 */
class RealVar : public Variable
{
public:
	/**
 	 * @brief constructor
 	 * build a new RealVar given an interval and a optionnaly a name
 	 */
	RealVar(const Interval & I = Interval::Universe, const std::string & name = "") 
		: Variable(new Interval(I), name),
		  m_digits(DefaultDigits)
	{
	}

	/**
 	 * pointer to the domain of the variable
 	 * @Return the interval domain
 	 */
	Interval domain() const
	{
		return(*(dynamic_cast<Interval *>(this->getDomain())));
	}

	/**
 	 * @brief print the value of the domain in a stream
 	 * @var os the output stream
 	 */
	void printDomain(std::ostream & os) const
	{
		Interval::SetOutputDigits(m_digits);
		//Interval::SetOutputDigits(20);

		this->getDomain()->print(os);
	}

	/**
 	 * @brief function to build the internal representation
 	 * it calls the factory class
 	 */
	virtual void buildImplementation(SearchTree *searchTree);

	virtual Variable *copy(SearchTree *searchTree) const;

	/**
 	 * Modification of the number of digits used for printing
 	 * @param n the number of digit used for printing
 	 */
	void setDigits(int n)
	{
		m_digits = n;
	}

	/**
 	 * Modification of the default number of digits used for printing
 	 * @param n the number of digit used for printing
 	 */
	static void SetDigits(int n)
	{
		DefaultDigits = n;
	}

	/// set the precision to reach
	void setPrecision(double p);

	// set the precision to reach for all new real variables
	static void SetPrecision(double p);

private:
	// @var precision for printing
	int m_digits;

	// @var default precision for printing
	static int DefaultDigits;

	// copy protection
	RealVar(const RealVar &);
	RealVar & operator=(const RealVar &);
};


/**
 * @ingroup RealVarImpl
 * @class RealVarImpl
 * @brief internal representation for RealVar
 */
class RealVarImpl : public VariableImpl
{
public:

	RealVarImpl(Variable *var, SearchTree *st, const Interval & i, int priority = 1) 
		: VariableImpl(var, st, sizeof(Interval), priority),
		  m_precision(DefaultPrecision)
	{
		VariableImpl::setDomain<Interval>(i);
	}
	/**
 	 * @brief constructor
 	 * @param var the symbolic representation of this
 	 */
	RealVarImpl(RealVar *var, SearchTree *st)
		: VariableImpl(var, st, sizeof(Interval), 1),
		  m_precision(DefaultPrecision)
	{
		VariableImpl::setDomain<Interval>(*(dynamic_cast<Interval *>(var->getDomain())));
	}

	/**
 	 * @brief Destructor
 	 */
	~RealVarImpl()
	{
	};

	void splitBox(const Interval & dom1,
	              const Interval & dom2) const
	{
		VariableImpl::splitBox<Interval>(dom1, dom2);
	}

	/**
 	 * give the type of the VariableImpl
 	 * @return a string given the name of the type
 	 */
	virtual std::string getType() const
	{
		return "RealVarImplementation";
	}

	/**
 	 * @brief Returns the current interval domain
 	 * @return the current interval domain
 	 */
	Interval domain() const;

	/**
 	 * Return the interval domain from a box
 	 * @return the interval domain from a box
 	 */
	Interval domain(Box *b) const;


	/**
 	 * Comparison between RealVarImplementation
 	 * @param v the other RealVarImplementation to compare with this
 	 * @pre this function works only if a SearchTree has been assigned to
 	 * the variable
 	 * @return true if this is less or equal than v
 	 */
	virtual bool
	operator<=(RealVarImpl & v) const
	{
		return(this->domain().width() >= v.domain().width());
	}

	/**
 	 * @name Management of variable domain with call of listeners
 	 */
	//@{
	/// The current domain takes value I
	virtual void setDomain(const Interval & I);

	/// The domain in box b takes value I
	virtual void setDomain(const Interval & I, Box *b);


	/// The current domain takes value I intersection current domain
	virtual void setInterDomain(const Interval & I);

	/// The current domain becomes empty
	void setEmptyDomain();
	//@}

	/**
 	 * @name Management of temporary domains
 	 * Temporary means that no event are generated
 	 */
	//@{

	/// I is the new domain in the current box
	virtual void setQuietDomain(const Interval & I);

	/// I is the new domain in box b
	virtual void setQuietDomain(const Interval & I, Box *b);


	/// Saves the current domain
	void saveDomain();

	/// Restores the last saved domain
	void restoreDomain();
	//@}

	/// Set the precision for splitting
	void setPrecision(double p);

	/// Returns the precision for splitting
	double precision() const;

	/**
 	 * @name Base class's virtual methods
 	 */
	//@{
	virtual bool isSplittable() const;

	virtual void split() const;
	virtual void split3() const;

	size_t size() const;

	virtual void toPoint(Box *b) const;

	virtual void updateSymbolicDomain() const;
	//@}

	/// Modification of the default splitting precision
	static void SetPrecision(double p);

private:
	// @var used to save/restore current domain
	Interval m_Isave;

	// @var used for splitting
	double m_precision;

	static double DefaultPrecision; // default precision

	// copy protection
	RealVarImpl & operator=(const RealVarImpl &);
	RealVarImpl(const RealVarImpl &);
};

/**
 * @ingroup RealConstraint
 * @class RealVarImplementationComparator
 * this class is a comparator between to RealVarImplementation
 */
class RealVarImplementationComparator : public VariableImplCmp
{
public:
	bool best(VariableImpl *v1, VariableImpl *v2)
	{
		return (*(static_cast<RealVarImpl *>(v1))) <= (*(static_cast<RealVarImpl *>(v2)));
	}
};


/**
 * @ingroup Interval
 * @class VecRealVar
 * @brief Vector of real variables
 */
class VecRealVar 
{
public:
	/// Exception class
	class OutOfRange { };

	/**
 	 * @name Constructors
 	 */
	//@{
	VecRealVar(int n,                             // size
	           const Interval & I,                // domain of each variable
	           const char *name = "");           // base name

	VecRealVar(int first,                         // first index
	           int n,                             // size
	           const Interval & I,                // domain of each variable
	           const char *name = "");           // base name
	//@}

	/// Destructor
	~VecRealVar();

	/// Size of vector
	int size() const;

	/// Returns the i-th variable (1 <= i <= size())
	RealVar &
	operator[](int i) const throw (VecRealVar::OutOfRange);

	/// Set the precision for splitting
	void setPrecision(double p);

	/// Number of digits used for domain printing
	void setDigits(int n);

	/// Computes in c the concatenation of init and n
	static void VecVarName(char *c,
	                       const char *init,
	                       int n);

private:
	RealVar **m_vars; // vector of variables
	int m_n;          // size
	int m_first;      // first index
};

END_NAMESPACE_ELISA

#endif // ELISA_REAL_VAR
